/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package modeloprediccion;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import motorprediccion.*;

/**
 *
 * @author diego
 */
public class ModeloPredictorCorrector extends Modelo{

    public int estimarTiempos(ArrayList<DatoPosicional> posiciones){
        //funcion que calcula las nuevas predicciones de acuerdo a las nuevas posiciones
        int cantActualizaciones = 0;
        //Solo para carga inicial
        try{
            this.guardarPrediccion();
        }
        catch (SQLException se) {
            System.out.println("Error en la BD");
        }
        //
        for (cantActualizaciones = 0; cantActualizaciones < posiciones.size(); cantActualizaciones++){
            Colectivo coleTemp = this.getColectivos(posiciones.get(cantActualizaciones).getIdColectivo());
            if (coleTemp == null){
                //Corregir la carga de recorrido
                coleTemp = new Colectivo(posiciones.get(cantActualizaciones).getIdColectivo(),recorridos.get(1));
                coleTemp.procesarNuevoDato(posiciones.get(cantActualizaciones));
            }
            //coleTemp.procesarNuevoDato(posiciones.get(cantActualizaciones));
            this.procesarNuevoTiempo(coleTemp, posiciones.get(cantActualizaciones));
            //colectivos.put(coleTemp.getIdColectivo(), coleTemp);
        }
        return cantActualizaciones;
    }

    public int obtenerPrediccion(Parada origen, Parada destino){
        //funcion que retorna el tiempo de prediccion entre dos paradas
        return (destino.getOrden() - origen.getOrden()) * 10;
    }
    
    public int calcularPrediccion(Parada origen, Parada destino){
        //funcion que calcula la prediccion entre dos paradas
        return (destino.getOrden() - origen.getOrden()) * 10;
    }
    
    public void procesarDatos(ArrayList<DatoProcesado> nuevosDatos) throws SQLException{
        //Funcion llamada desde el main
        int i = 0;
        this.guardarPrediccion();
    }
    
    public void guardarPrediccion() throws SQLException {
        //Esta funcion guarda las nuevas estimaciones en la Base de Datos
        Connection conexionBD = null;
        Statement s;
        String consulta;
        GregorianCalendar ahora = new GregorianCalendar();
        float prediccion;
        
        try {
            conexionBD = DriverManager.getConnection("jdbc:postgresql://localhost/sistemaprediccion",
                                            "postgres", "postgres");
            s = conexionBD.createStatement();
            consulta = "DELETE FROM predictorcorrector;";
            s.executeUpdate(consulta);
            for(int recorridoActual=0; recorridoActual< recorridos.size(); recorridoActual++) {
                for (int paradaActual = 0; paradaActual < recorridos.get(recorridoActual).getCantidadParadas()-1;paradaActual++){
                    //se debe actualizar de acuerdo a la prediccion
                    prediccion = this.calcularPrediccion(this.recorridos.get(recorridoActual).obtenerParadaOrden(paradaActual), this.recorridos.get(recorridoActual).obtenerParadaOrden(paradaActual+1)); 
                    consulta = "INSERT INTO predictorcorrector("+
                                "parada_origen, parada_destino, tiempo_estimado, fecha_actualizacion)"+
                                "VALUES ("+
                                this.recorridos.get(recorridoActual).obtenerParadaOrden(paradaActual).getId()+", "+
                                this.recorridos.get(recorridoActual).obtenerParadaOrden(paradaActual+1).getId() + ", "+
                                prediccion + ","+
                                "to_timestamp("+ahora.getTimeInMillis()/1000+"));\n";
                    s.executeUpdate(consulta);
                }
            }
        }
        catch (SQLException se) {
          System.out.println("Error en la BD");
          se.printStackTrace();
          System.exit(1);
        }
        conexionBD.close();
    }
    
    public void guardarPrediccion(Parada parada, double prediccion) throws SQLException {
        //Esta funcion guarda una estimacion en la Base de Datos
        Connection conexionBD = null;
        Statement s;
        String consulta;
        GregorianCalendar ahora = new GregorianCalendar();
        
        try {
            conexionBD = DriverManager.getConnection("jdbc:postgresql://localhost/sistemaprediccion",
                                            "postgres", "postgres");
            s = conexionBD.createStatement();
            consulta = "DELETE FROM predictorcorrector WHERE parada_origen ="+parada.getId()+";";
            s.executeUpdate(consulta);
            consulta = "INSERT INTO predictorcorrector("+
                        "parada_origen, parada_destino, tiempo_estimado, fecha_actualizacion)"+
                        "VALUES ("+
                        parada.getId()+", 1, "+
                        prediccion + ","+
                        "to_timestamp("+ahora.getTimeInMillis()/1000+"));\n";
            s.executeUpdate(consulta);
        }
        catch (SQLException se) {
          System.out.println("Error en la BD");
          se.printStackTrace();
          System.exit(1);
        }
        conexionBD.close();
    }
    
    int procesarNuevoTiempo(Colectivo colectivo, DatoPosicional posicion){
        int indexRecorrido = 0;
        Parada nuevaParada;
        while (indexRecorrido < this.recorridos.size() && this.recorridos.get(indexRecorrido).getId() != colectivo.getRecorrido())
            indexRecorrido++;
        if (indexRecorrido == this.recorridos.size()) 
            return -1; //No existe o no se encontro recorrido
        else {
            if (colectivo.obtenerParadaActual() == null){ //es el primer dato del colectivo
                this.colectivos.put(colectivo.getIdColectivo(), colectivo);
                this.colectivos.get(colectivo.getIdColectivo()).procesarNuevoDato(posicion);
            }
            else{ // falta calcular la tolerancia de tiempo
                Recorrido rTemp = this.recorridos.get(indexRecorrido);
                nuevaParada = rTemp.encontrarParadaCercana(posicion.getPosicion(), colectivo.obtenerParadaActual().getOrden());
                if (nuevaParada != null){
                    long diferenciaTiempo = new GregorianCalendar().getTimeInMillis() - colectivo.getHoraArriboParadaActual().getTimeInMillis();
                    float diferenciaDistancia = rTemp.distancia(colectivo.obtenerParadaActual(), nuevaParada);
                    Parada Temp1 = colectivo.obtenerParadaActual();
                    for (int paso = colectivo.obtenerParadaActual().getOrden(); paso < nuevaParada.getOrden(); paso++){
                        Parada Temp2 = rTemp.obtenerParadaOrden(Temp1.getOrden()); //el orden de la parada es uno mayor a su posicion en el arreglo
                        double prediccion = diferenciaTiempo * rTemp.distancia(Temp1, Temp2)/ diferenciaDistancia;
                        prediccion = prediccion * 0.8 + 0.2 * this.obtenerPrediccion(Temp1, Temp2);
                        try{
                            this.guardarPrediccion(Temp1, prediccion);
                        }
                        catch (SQLException se) {
                            System.out.println("Error al guardar prediccion para parada "+ Temp1.getId());
                        }
                        Temp1 = Temp2;
                    }
                }
            }
        }
        return 0;
    }
}
